package com.web.kdl.config.security.components;

import com.alibaba.fastjson2.JSON;
import com.web.kdl.common.result.CommonResult;
import com.web.kdl.common.result.ReturnCode;
import com.web.kdl.config.EmailConfig;
import com.web.kdl.config.security.AuthConstants;
import com.web.kdl.config.security.UserDetailService;
import com.web.kdl.pojo.form.UserForm;
import com.web.kdl.service.email.IEmailService;
import com.web.kdl.service.user.UserService;
import com.web.kdl.util.JwtTokenUtil;
import com.web.kdl.util.RedisUtils;
import com.web.kdl.util.ServletUtils;
import com.web.kdl.util.SpringContextUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.web.kdl.config.security.AuthConstants.*;
/**
 * @Author： shulibin
 * @Date： 2025/7/28 10:19
 * @Describe：
 */
@Slf4j
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
    private final RedisUtils redisUtils;
    private final Long expiration;

    public LoginFilter(AuthenticationManager authenticationManager, RedisUtils redisUtils, Long expiration) {
        this.expiration = expiration;
        this.redisUtils = redisUtils;
        super.setAuthenticationManager(authenticationManager);
        super.setPostOnly(true);
        super.setFilterProcessesUrl("/login");
        super.setUsernameParameter("username");
        super.setPasswordParameter("password");
    }

    @SneakyThrows
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        UserForm userInfo = JSON.parseObject(request.getInputStream(), StandardCharsets.UTF_8, UserForm.class);
        request.setAttribute("username", userInfo.getUsername());
        return super.getAuthenticationManager().authenticate(
                new UsernamePasswordAuthenticationToken(userInfo.getUsername(), userInfo.getPassword())
        );
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        log.info("YaLoginFilter authentication success: {}", authResult);
        // 如果验证成功, 就生成Token并返回
        AuthUser authUser = (AuthUser) authResult.getPrincipal();
        String userId = authUser.getUserId();
        String username = authUser.getUsername();
        String token = JwtTokenUtil.generateToken(userId);
        response.setHeader(TOKEN_HEADER, token);
        // 将token存入Redis中
        redisUtils.set(REDIS_KEY_AUTH_TOKEN + userId, token, expiration);
        
        // 登录成功，清除失败次数、锁定状态和锁定历史记录
        redisUtils.delete(AuthConstants.REDIS_KEY_LOGIN_FAILURE_COUNT + username);
        redisUtils.delete(AuthConstants.REDIS_KEY_LOGIN_LOCK + username);
        redisUtils.delete(AuthConstants.REDIS_KEY_LOGIN_LOCK + username + ":history");
        
        // 将UserDetails存入redis中
        // 5. 构建完整的用户信息存储到Redis
        Map<String, Object> userDetailMap = new HashMap<>();
        userDetailMap.put("userId", userId);
        userDetailMap.put("username", authUser.getUsername());
        userDetailMap.put("password", "[PROTECTED]"); // 密码不存储明文
        userDetailMap.put("email", authUser.getEmail());
        userDetailMap.put("role", authUser.getRole());
        userDetailMap.put("authorities", authUser.getAuthorities());

        // 6. 存储用户详情（使用userId作为key）
        redisUtils.set(REDIS_KEY_AUTH_USER_DETAIL + userId,
                JSON.toJSONString(userDetailMap),
                1, TimeUnit.DAYS);

        Map<String, Object> result = new HashMap<>();
        result.put("userId", authUser.getUserId());
        result.put("username", authUser.getUsername());
        result.put("access_token", token);
        ServletUtils.renderResult(response,CommonResult.success(result));
        log.info("LoginFilter authentication end");
    }
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed) throws IOException {
        log.info("LoginFilter authentication failed: {}", failed.getMessage());
        
        // 获取用户名
        String username = (String) request.getAttribute("username");
        if (username == null) {
            username = request.getParameter("username");
        }
        
        if (username != null) {
            // 记录登录失败次数
            String failureCountKey = AuthConstants.REDIS_KEY_LOGIN_FAILURE_COUNT + username;
            String lockKey = AuthConstants.REDIS_KEY_LOGIN_LOCK + username;
            
            // 检查用户是否已经被锁定
            Boolean isLocked = redisUtils.getBoolean(lockKey);
            if (Boolean.TRUE.equals(isLocked)) {
                ServletUtils.renderResult(response, new CommonResult<>(ReturnCode.UNAUTHORIZED.getCode(), "账号已被锁定，请5分钟后重试"));
                return;
            }
            
            // 检查是否是锁定解除后首次失败
            Boolean hasLockHistory = redisUtils.hasKey(lockKey + ":history");
            if (Boolean.TRUE.equals(hasLockHistory)) {
                // 锁定解除后首次失败，生成新密码并发送邮件
                try {
                    sendResetPasswordEmail(username);
                    // 清除锁定历史记录
                    redisUtils.delete(lockKey + ":history");
                    ServletUtils.renderResult(response, new CommonResult<>(ReturnCode.UNAUTHORIZED.getCode(), "密码错误，已发送重置密码邮件到您的邮箱"));
                } catch (Exception e) {
                    log.error("发送重置密码邮件失败: {}", e.getMessage());
                    ServletUtils.renderResult(response, new CommonResult<>(ReturnCode.UNAUTHORIZED.getCode(), "密码错误，发送重置密码邮件失败，请联系管理员"));
                }
                return;
            }
            
            // 增加失败次数
            Long count = redisUtils.increment(failureCountKey, 1);
            if (count == null) {
                count = 1L;
                redisUtils.set(failureCountKey, count, AuthConstants.LOCK_TIME);
            }
            
            // 检查是否达到最大失败次数
            if (count >= AuthConstants.MAX_LOGIN_FAILURE_COUNT) {
                // 锁定用户5分钟
                redisUtils.set(lockKey, true, AuthConstants.LOCK_TIME);
                // 清除失败次数
                redisUtils.delete(failureCountKey);
                // 设置锁定历史记录，用于标记用户被锁定过
                redisUtils.set(lockKey + ":history", true, AuthConstants.LOCK_TIME_10);
                ServletUtils.renderResult(response, new CommonResult<>(ReturnCode.UNAUTHORIZED.getCode(), "密码错误次数过多，账号已被锁定，请5分钟后重试"));
                return;
            }
            
            // 返回剩余尝试次数
            int remaining = AuthConstants.MAX_LOGIN_FAILURE_COUNT - count.intValue();
            ServletUtils.renderResult(response, new CommonResult<>(ReturnCode.UNAUTHORIZED.getCode(), "登录失败：" + failed.getMessage() + "，剩余尝试次数：" + remaining));
        } else {
            ServletUtils.renderResult(response, new CommonResult<>(ReturnCode.UNAUTHORIZED.getCode(), "登录失败：" + failed.getMessage()));
        }
    }
    
    /**
     * 发送重置密码邮件
     */
    private void sendResetPasswordEmail(String username) throws Exception {
        // 获取用户信息
        UserDetailService userDetailService = SpringContextUtils.getBean(UserDetailService.class);
        AuthUser authUser = (AuthUser) userDetailService.loadUserByUsername(username);
        String email = authUser.getEmail();
        
        // 生成随机密码
        String newPassword = generateRandomPassword();
        
        // 更新用户密码
        UserService userService = SpringContextUtils .getBean(UserService.class);
        userService.updatePassword(username, newPassword);
        
        // 发送邮件
        IEmailService emailService = SpringContextUtils.getBean(IEmailService.class);
        String subject = "密码重置通知";
        TemplateEngine templateEngine = SpringContextUtils.getBean(TemplateEngine.class);
        Context context = new Context();
        context.setVariable("username", username);
        context.setVariable("password", newPassword);
        String content = templateEngine.process("template.html", context);
        EmailConfig emailConfig = SpringContextUtils.getBean(EmailConfig.class);
        emailService.sendHtml(emailConfig.getSenderEmail(), email, subject, content);
        
        // 清除重置密码标记
        redisUtils.delete(AuthConstants.REDIS_KEY_PASSWORD_RESET + username);
    }
    
    /**
     * 生成随机密码
     */
    private String generateRandomPassword() {
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        StringBuilder sb = new StringBuilder(8);
        for (int i = 0; i < 8; i++) {
            int index = (int) (Math.random() * chars.length());
            sb.append(chars.charAt(index));
        }
        return sb.toString();
    }
}
